compile到driver

// dex2oat.cc/art::Dex2oat()
static dex2oat::ReturnCode Dex2oat(int argc, char** argv) {
  b13564922();

  TimingLogger timings("compiler", false, false);

  // Allocate `dex2oat` on the heap instead of on the stack, as Clang
  // might produce a stack frame too large for this function or for
  // functions inlining it (such as main), that would not fit the
  // requirements of the `-Wframe-larger-than` option.
  std::unique_ptr<Dex2Oat> dex2oat = std::make_unique<Dex2Oat>(&timings);

  // Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError.
  dex2oat->ParseArgs(argc, argv);

  // If needed, process profile information for profile guided compilation.
  // This operation involves I/O.
  if (dex2oat->UseProfile()) {
    if (!dex2oat->LoadProfile()) {
      LOG(ERROR) << "Failed to process profile file";
      return dex2oat::ReturnCode::kOther;
    }
  }

  art::MemMap::Init();  // For ZipEntry::ExtractToMemMap, and vdex.

  // Check early that the result of compilation can be written
  if (!dex2oat->OpenFile()) {
    return dex2oat::ReturnCode::kOther;
  }


  dex2oat::ReturnCode setup_code = dex2oat->Setup();
  if (setup_code != dex2oat::ReturnCode::kNoFailure) {
    dex2oat->EraseOutputFiles();
    return setup_code;
  }

  // TODO: Due to the cyclic dependencies, profile loading and verifying are
  // being done separately. Refactor and place the two next to each other.
  // If verification fails, we don't abort the compilation and instead log an
  // error.
  // TODO(b/62602192, b/65260586): We should consider aborting compilation when
  // the profile verification fails.
  // Note: If dex2oat fails, installd will remove the oat files causing the app
  // to fallback to apk with possible in-memory extraction. We want to avoid
  // that, and thus we're lenient towards profile corruptions.
  if (dex2oat->UseProfile()) {
    dex2oat->VerifyProfileData();
  }

  dex2oat::ReturnCode result;
  if (dex2oat->IsImage()) {
    result = CompileImage(*dex2oat);
  } else {
    result = CompileApp(*dex2oat);
  }

  return result;
}
...

根据编译的对象是系统image还是用户app,分别调用CompileImageCompileApp。它们都返回class_loader编译好的方法就存放在class_loader中。

CompileImageCompileApp内部都通过dex2oat::Compiler实现,其也返回class_loader

dex2oat::Compile()在设置好driver_指针后,调用CompileDexFile()。它接受一个存储指向DexFile的指针的vector引用,并返回class_loader

成员driver_CompilerDriver类型的指针。编译的信息存储在CompilerDriver对象中,Compile()函数new了一个新对象并reset此指针完成配置。然后CompileDexFiles(dex_files);完成编译。

CompileDexFiles()中,调用了:

driver->Precompile(...);
...
driver->CompileAll(class_loader, dex_files, timings_);
...

虽然这两个函数都没有返回值,但是信息(包括编译好的方法)都存储在了driver_class_loader中。

接下来控制流进入CompilerDriver类的代码。

driver分析

CompilerDriver类的实现位于art/dex2oat/driveri/compiler_driver.h(.cc)中。

CompileAll的功能是编译所有能编译的类和方法。内部调用了Compile函数,Compile函数的功能也是编译所有能编译的类和方法。

Compile中调用了两次CompileDexFile成员函数,用于编译单个 dex file 两次的区别在于最后一个参数——回调函数。CompileMethodQuick将dex编译为oat,CompileMethodDex2Dex对dex进行优化。

// compiler_driver.cc

程序通过context.ForAllLambda发起线程执行编译,最后控制流转入回调函数参数。

CompileMethodQuick

CompileMethodQuick构造quick_fnlambda函数。quick_fn返回编译好的方法,在执行时完成包括是否应该profile guide compilation的检查。如果编译器拒绝编译或者编译不成功,则返回null

quick_fn内部根据要编译的方法类型,调用compiled_method=driver->GetCompiler()->JniCompile()compiled_method=driver->GetCompiler()->Compile()编译方法。

CompileMethodDex2Dex同理。

最后两个函数都通过让CompileMethodHarness调用quick_fn完成编译。

CompileMethodHarness如果quick_fn返回值不为nullptr,则将对应的编译后方法家人dirver中。

if (compiled_method != nullptr) {
    driver->AddCompiledMethod(method_ref, compiled_method);
}

总结

ART编译是通过逐级分解的方式进行的。从CompileImage|CompileAppCompileAll再到Compile,都是以一整个Image/App为输入(表示为一系列dex file)。在Compile中,对逐个dex file调用CompileDexFile进行编译,对象是单个dex file。在CompileDexFile中,对逐个method调用compile_fnCompileMethodQuickCompileMtehodDex2Dex进行编译,对象是单个methhod,最后完成编译。